package trackClustering.example.behavior;
import trackClustering.example.entity.TrajectoryPoint;
import java.util.*;
import java.util.stream.Collectors;

public class TrajectoryProcessByBehaviorDbacan {
    private double behaviorEps;
    private final int behaviorMinPts;

    public TrajectoryProcessByBehaviorDbacan(double behaviorEps, int behaviorMinPts) {
        this.behaviorEps = behaviorEps;
        this.behaviorMinPts = behaviorMinPts;
    }

    /**
     * 行为分层聚类主方法
     */
    // 在 TrajectoryProcessByBehavior.java 中
    public Map<Integer, Map<Integer, List<TrajectoryPoint>>> clusterBySpatialClusters(Map<Integer, List<TrajectoryPoint>> spatialClusters) {
        Map<Integer, Map<Integer, List<TrajectoryPoint>>> result = new HashMap<>();

        int globalClusterId = 1;
        for (Map.Entry<Integer, List<TrajectoryPoint>> entry : spatialClusters.entrySet()) {
            int spatialClusterId = entry.getKey();
            List<TrajectoryPoint> spatialCluster = entry.getValue();

            if (spatialCluster.size() < behaviorMinPts) {
                continue;
            }

            // 行为聚类逻辑（与原 cluster() 方法相同）
            List<BehaviorFeature> features = extractBehaviorFeatures(spatialCluster);
            if(behaviorEps<0){
                this.behaviorEps = autoTuneEps(features); // 自动设置Eps
            }
            Map<Integer, List<BehaviorFeature>> behaviorClusters = behaviorDBSCAN(features);

            Map<Integer, List<TrajectoryPoint>> behaviorClustersMap = new HashMap<>();
            for (Map.Entry<Integer, List<BehaviorFeature>> behaviorEntry : behaviorClusters.entrySet()) {
                List<TrajectoryPoint> pointsInCluster = behaviorEntry.getValue().stream()
                        .map(BehaviorFeature::getPoint)
                        .collect(Collectors.toList());
                behaviorClustersMap.put(globalClusterId++, pointsInCluster);
            }

            result.put(spatialClusterId, behaviorClustersMap);
        }
        System.out.println("行为minPts: " + behaviorMinPts);
        System.out.println("行为eps: " + behaviorEps);
        return result;
    }


    // 行为特征类
    private static class BehaviorFeature {
        private final TrajectoryPoint point;
        private final double[] features;

        // 在BehaviorFeature构造函数中使用时间戳：
        public BehaviorFeature(TrajectoryPoint point,
                               double timeMean, double timeStd,
                               double speedMean, double speedStd) {
            this.point = point;
            // 标准化时间与速度
            double time = (point.getTimeStamp() - timeMean) / timeStd; // 使用时间戳
            double speed = (point.getHorizontalSpeed() - speedMean) / speedStd;
            this.features = new double[]{time, speed};
        }


        public double distanceTo(BehaviorFeature other) {
            double sum = 0.0;
            for (int i = 0; i < features.length; i++) {
                sum += Math.pow(this.features[i] - other.features[i], 2);
            }
            return Math.sqrt(sum);
        }

        public TrajectoryPoint getPoint() {
            return point;
        }
    }

    // 特征提取与标准化
    // 在TrajectoryProcessByBehavior类中修改以下部分：
    // 特征提取与标准化方法
    private List<BehaviorFeature> extractBehaviorFeatures(List<TrajectoryPoint> points) {
        List<TrajectoryPoint> sortedPoints = points.stream()
                .sorted(Comparator.comparingLong(TrajectoryPoint::getTimeStamp))
                .collect(Collectors.toList());

        // 直接将整个空间簇视为一个segment（无需分割）
        List<List<TrajectoryPoint>> segments = Collections.singletonList(sortedPoints);

        List<BehaviorFeature> features = new ArrayList<>();
        for (List<TrajectoryPoint> segment : segments) {
            double timeMean = calculateMean(segment.stream().mapToLong(TrajectoryPoint::getTimeStamp).boxed().collect(Collectors.toList()));
            double timeStd = calculateStd(segment.stream().mapToLong(TrajectoryPoint::getTimeStamp).boxed().collect(Collectors.toList()));
            double speedMean = calculateMean(segment.stream().mapToDouble(TrajectoryPoint::getHorizontalSpeed).boxed().collect(Collectors.toList()));
            double speedStd = calculateStd(segment.stream().mapToDouble(TrajectoryPoint::getHorizontalSpeed).boxed().collect(Collectors.toList()));

            // 特征标准化时需处理标准差为0的情况（避免除零错误）
            features.addAll(segment.stream()
                    .map(p -> new BehaviorFeature(
                            p,
                            timeMean, Math.max(timeStd, 1e-6), // 防止timeStd为0
                            speedMean, Math.max(speedStd, 1e-6) // 防止speedStd为0
                    ))
                    .collect(Collectors.toList()));
        }
        return features;
    }





    // 新增时间均值计算方法
    private double calculateMean(List<? extends Number> values) {
        return values.stream()
                .mapToDouble(Number::doubleValue)
                .average()
                .orElse(0);
    }


    // 重写标准差计算方法（支持数值列表）

    private double calculateStd(List<? extends Number> values) {
        double mean = calculateMean(values);
        double sum = 0.0;
        for (Number val : values) {
            sum += Math.pow(val.doubleValue() - mean, 2);
        }
        return Math.sqrt(sum / (values.size() - 1));
    }



    // 行为DBSCAN核心实现
    private Map<Integer, List<BehaviorFeature>> behaviorDBSCAN(List<BehaviorFeature> features) {
        Map<Integer, List<BehaviorFeature>> clusters = new HashMap<>();
        Set<BehaviorFeature> visited = new HashSet<>();
        int clusterId = 0;

        for (BehaviorFeature feature : features) {
            if (!visited.contains(feature)) {
                visited.add(feature);
                List<BehaviorFeature> neighbors = getNeighbors(feature, features);
                if (neighbors.size() < behaviorMinPts) {
                    // 噪声点，忽略
                } else {
                    clusterId++;
                    expandCluster(clusters, clusterId, feature, neighbors, features, visited);
                }
            }
        }
        return clusters;
    }

    // 获取邻近点
    private List<BehaviorFeature> getNeighbors(BehaviorFeature center, List<BehaviorFeature> features) {
        return features.stream()
                .filter(f -> f.distanceTo(center) <= behaviorEps)
                .collect(Collectors.toList());
    }

    // 扩展簇
    private void expandCluster(Map<Integer, List<BehaviorFeature>> clusters, int clusterId,
                               BehaviorFeature center, List<BehaviorFeature> neighbors,
                               List<BehaviorFeature> allFeatures, Set<BehaviorFeature> visited) {
        clusters.computeIfAbsent(clusterId, k -> new ArrayList<>()).add(center);
        Set<BehaviorFeature> toCheck = new HashSet<>(neighbors);

        while (!toCheck.isEmpty()) {
            BehaviorFeature current = toCheck.iterator().next();
            toCheck.remove(current);

            if (!visited.contains(current)) {
                visited.add(current);
                List<BehaviorFeature> currentNeighbors = getNeighbors(current, allFeatures);
                if (currentNeighbors.size() >= behaviorMinPts) {
                    toCheck.addAll(currentNeighbors);
                }
            }

            if (!clusters.values().stream().anyMatch(list -> list.contains(current))) {
                clusters.get(clusterId).add(current);
            }
        }
    }
    private double autoTuneEps(List<BehaviorFeature> features) {
        int k = behaviorMinPts - 1;
        List<Double> kDistances = new ArrayList<>();

        for (BehaviorFeature f1 : features) {
            List<Double> distances = new ArrayList<>();
            for (BehaviorFeature f2 : features) {
                if (f1 != f2) {
                    distances.add(f1.distanceTo(f2));
                }
            }
            if (distances.size() >= k) {
                Collections.sort(distances);
                kDistances.add(distances.get(k));
            }
        }
        return getQuantile(kDistances, 1);
    }


    private double getQuantile(List<Double> values, double quantile) {
        Collections.sort(values);
        // 修改此处：使用 (values.size() - 1) * quantile
        int index = (int) Math.floor((values.size() - 1) * quantile);
        return values.get(index);
    }

}
